/************************************************************************
Copyright (c) 2010, 2013, C.R. All rights reserved. 

E-mail: crtoyou@gmail.com; crtoyou@163.com

You can redistribute it and/or modify it under the terms of the
GNU Lesser General Public License as published by the
Free Software Foundation version 3 of the License.

This source code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. if not, write to the Free Software Foundation, Inc., 
51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
************************************************************************/

#ifndef __CR_THREAD_CTRL_BASE_H__
#define __CR_THREAD_CTRL_BASE_H__

#include <Windows.h>
#include <crsyncque.h>
#include <crdebug.h>

namespace cr {

class threadbase 
{
private:
	// disable copy
	threadbase(const threadbase&);
	threadbase& operator=(const threadbase&);

public:
	threadbase(void)
		: m_idThread(0)
		, m_hThread(NULL)
		, m_hEvent(NULL)
		, m_timeout(-1)
		, m_bRun(false)
	{}

	virtual ~threadbase(void)
	{
		destroy();
	}

	virtual bool init(bool bstart = false, int timeout = -1)
	{
		ASSERT(!m_bRun);
		m_hEvent = CreateEvent(0, TRUE, FALSE, 0); 
		m_timeout = timeout;
		m_bRun = TRUE;
		m_hThread = CreateThread(0, 0, thd, this, 0, &m_idThread);
		if (bstart) 
			start();
		else 
			pause();
		return true;
	}

	virtual bool destroy(int waittime = 5000)
	{
		if (m_bRun) {
			m_bRun = FALSE;
			unblock();
			if (!waitend(waittime))
				TerminateThread(m_hThread, -1);
			CloseHandle(m_hThread);
			m_hThread = NULL;
			CloseHandle(m_hEvent);
			m_hEvent = NULL;
		}
		return true;
	}

	virtual void unblock()
	{
		SetEvent(m_hEvent);
	}

	inline bool waitend(int timeout = -1) 
	{
		int ret = WaitForSingleObject(m_hThread, timeout);
		return WAIT_OBJECT_0 == ret;
	}

	void start() {SetEvent(m_hEvent);}
	void pause() {ResetEvent(m_hEvent);}

	inline void settimeout(int timeout) {m_timeout = timeout;}
	inline int gettimeout() const {return m_timeout;}
	inline DWORD threadid() const {return m_idThread;}
	inline HANDLE safehandle() const {return m_hThread;}

protected:
	virtual bool pump() = 0;
	virtual void idle() {}
	virtual int quit(int code) {m_bRun = false; return code;}

private:
	HANDLE m_hThread;
	DWORD m_idThread;
	volatile HANDLE m_hEvent;
	volatile bool m_bRun;
	volatile int m_timeout;

	static DWORD WINAPI thd(LPVOID pParam)
	{
		threadbase* obj = (threadbase*)pParam;
		return obj->run();
	}

	DWORD run()
	{
		while (m_bRun)
		{
			int ret = WaitForSingleObject(m_hEvent, m_timeout);
			if (!m_bRun)
				return quit(0);
			else if (WAIT_TIMEOUT == ret)
				idle();
			else if (WAIT_OBJECT_0 != ret)
				return quit(-1);
			else if (!pump())
				return quit(0);
		}
		return quit(0);
	}
};


class commandtarget : public threadbase
{
public:
	enum { cmd_none = -1 };

	struct cmd_t
	{
		int msgcode;
		void* param1;
		void* param2;
		cmd_t(int code = cmd_none, void* para1 = NULL, void* para2 = NULL)
			: msgcode(code), param1(para1), param2(para2)
		{}
	};

public:
	commandtarget(int size = 50)
		: que(50)
	{}

	bool sendcmd(int code, void* para1, void* para2)
	{
		return oncommand(code, para1, para2);
	}

	bool postcmd(int code, void* para1, void* para2)
	{
		que.put(cmd_t(code, para1, para2));
		start();
		return true;
	}

protected:
	virtual bool pump()
	{
		cmd_t msg;
		if (!que.get(msg))
			return true;
		oncommand(msg.msgcode, msg.param1, msg.param2);
		//ignore perhaps false returned by oncommand. 
		return true;
	}

	virtual bool oncommand(int code, void* param1, void* param2) {}

private:
	sync_que<cmd_t> que;
};

}//namespace

#endif //__CR_THREAD_CTRL_BASE_H__